from insts.inst import NoOperandInstruction
from jthread.operand_stack import OperandStack


class OPE(NoOperandInstruction):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.operation = ''
        self.kind = None

    def execute(self, frame):
        stack: OperandStack = frame.getOperandStack()
        if self.operation == 'add':
            self.add(stack)
        elif self.operation == 'mul':
            self.mul(stack)
        elif self.operation == 'div':
            self.div(stack)
        elif self.operation == 'and':
            self.and_(stack)
        elif self.operation == 'or':
            self.or_(stack)
        elif self.operation == 'xor':
            self.xor(stack)
        elif self.operation == 'neg':
            self.neg(stack)
        elif self.operation == 'rem':
            self.rem(stack)
        elif self.operation == 'lsh':
            self.lsh(stack)
        elif self.operation == 'rsh':
            self.rsh(stack)
        elif self.operation == 'ursh':
            self.ursh(stack)
        elif self.operation == 'sub':
            self.sub(stack)
        else:
            raise ValueError('Invalid operation')

    def add(self, stack):
        if self.kind == 'int':
            val1 = stack.popInt()
            val2 = stack.popInt()
            stack.pushInt(val1 + val2)
        elif self.kind == 'long':
            val1 = stack.popLong()  # 8 bytes
            val2 = stack.popLong()
            stack.pushLong(val1 + val2)

        elif self.kind == 'float':
            val1 = stack.popFloat()
            val2 = stack.popFloat()
            stack.pushFloat(val1 + val2)

        elif self.kind == 'double':
            val1 = stack.popDouble()
            val2 = stack.popDouble()
            stack.pushDouble(val1 + val2)

        else:
            raise ValueError('Invalid kind of operand')

    def mul(self, stack):
        if self.kind == 'int':
            val1 = stack.popInt()
            val2 = stack.popInt()
            stack.pushInt(val1 * val2)
        elif self.kind == 'long':
            val1 = stack.popLong()  # 8 bytes
            val2 = stack.popLong()
            stack.pushLong(val1 * val2)

        elif self.kind == 'float':
            val1 = stack.popFloat()
            val2 = stack.popFloat()
            stack.pushFloat(val1 * val2)

        elif self.kind == 'double':
            val1 = stack.popDouble()
            val2 = stack.popDouble()
            stack.pushDouble(val1 * val2)

        else:
            raise ValueError('Invalid kind of operand')

    def div(self, stack):
        if self.kind == 'int':
            val2 = stack.popInt()
            val1 = stack.popInt()
            if val2 == 0:
                raise ValueError('Divide by zero')
            stack.pushInt(val1 / val2)
        elif self.kind == 'long':
            val2 = stack.popLong()  # 8 bytes
            val1 = stack.popLong()
            if val2 == 0:
                raise ValueError('Divide by zero')
            stack.pushLong(val1 / val2)

        elif self.kind == 'float':
            val2 = stack.popFloat()
            val1 = stack.popFloat()
            if val2 == 0.0:
                raise ValueError('Divide by zero')
            stack.pushFloat(val1 / val2)

        elif self.kind == 'double':
            val2 = stack.popDouble()
            val1 = stack.popDouble()
            if val2 == 0.0:
                raise ValueError('Divide by zero')
            stack.pushDouble(val1 / val2)

        else:
            raise ValueError('Invalid kind of operand')

    def and_(self, stack):
        if self.kind == 'int':
            val2 = stack.popInt()
            val1 = stack.popInt()
            stack.pushInt(val1 & val2)

        elif self.kind == 'long':
            val2 = stack.popLong()  # 8 bytes
            val1 = stack.popLong()
            stack.pushLong(val1 & val2)
        else:
            raise ValueError('Invalid kind of operand')

    def or_(self, stack):
        if self.kind == 'int':
            val2 = stack.popInt()
            val1 = stack.popInt()
            stack.pushInt(val1 | val2)

        elif self.kind == 'long':
            val2 = stack.popLong()  # 8 bytes
            val1 = stack.popLong()
            stack.pushLong(val1 | val2)
        else:
            raise ValueError('Invalid kind of operand')

    def xor(self, stack):
        if self.kind == 'int':
            val2 = stack.popInt()
            val1 = stack.popInt()
            stack.pushInt(val1 ^ val2)

        elif self.kind == 'long':
            val2 = stack.popLong()  # 8 bytes
            val1 = stack.popLong()
            stack.pushLong(val1 ^ val2)
        else:
            raise ValueError('Invalid kind of operand')

    def neg(self, stack):
        if self.kind == 'int':
            val1 = stack.popInt()
            stack.pushInt(-val1)
        elif self.kind == 'long':
            val1 = stack.popLong()
            stack.pushLong(-val1)

        elif self.kind == 'float':
            val1 = stack.popFloat()
            stack.pushFloat(-val1)

        elif self.kind == 'double':
            val1 = stack.popDouble()
            stack.pushDouble(-val1)
        else:
            raise ValueError('Invalid kind of operand')

    def rem(self, stack):
        if self.kind == 'int':
            val2 = stack.popInt()
            val1 = stack.popInt()
            if val2 == 0:
                raise ValueError('Divide by zero')
            stack.pushInt(val1 % val2)
        elif self.kind == 'long':
            val2 = stack.popLong()  # 8 bytes
            val1 = stack.popLong()
            if val2 == 0:
                raise ValueError('Divide by zero')
            stack.pushLong(val1 % val2)

        elif self.kind == 'float':
            val2 = stack.popFloat()
            val1 = stack.popFloat()
            if val2 == 0.0:
                raise ValueError('Divide by zero')
            stack.pushFloat(val1 % val2)

        elif self.kind == 'double':
            val2 = stack.popDouble()
            val1 = stack.popDouble()
            if val2 == 0.0:
                raise ValueError('Divide by zero')
            stack.pushDouble(val1 % val2)

        else:
            raise ValueError('Invalid kind of operand')

    def lsh(self, stack):
        if self.kind == 'int':
            val2 = stack.popInt()
            val1 = stack.popInt()
            stack.pushInt(val1 << val2)
        elif self.kind == 'long':
            val2 = stack.popInt()
            val1 = stack.popLong()
            stack.pushLong(val1 << val2)
        else:
            raise ValueError('Invalid kind of operand')

    def rsh(self, stack):
        if self.kind == 'int':
            val2 = stack.popInt()
            val1 = stack.popInt()
            stack.pushInt(val1 >> val2)
        elif self.kind == 'long':
            val2 = stack.popInt()
            val1 = stack.popLong()
            stack.pushLong(val1 >> val2)
        else:
            raise ValueError('Invalid kind of operand')

    def ursh(self, stack):
        if self.kind == 'int':
            val2 = stack.popInt() & 0x1f  # unsigned shift
            val1 = stack.popInt()
            stack.pushInt(val1 >> val2)
        elif self.kind == 'long':
            val2 = stack.popInt()
            val1 = stack.popLong()
            stack.pushLong(val1 >> val2)
        else:
            raise ValueError('Invalid kind of operand')

    def sub(self, stack):
        if self.kind == 'int':
            val2 = stack.popInt()
            val1 = stack.popInt()
            stack.pushInt(val1 - val2)
        elif self.kind == 'long':
            val2 = stack.popLong()  # 8 bytes
            val1 = stack.popLong()
            stack.pushLong(val1 - val2)

        elif self.kind == 'float':
            val2 = stack.popFloat()
            val1 = stack.popFloat()
            stack.pushFloat(val1 - val2)

        elif self.kind == 'double':
            val2 = stack.popDouble()
            val1 = stack.popDouble()
            stack.pushDouble(val1 - val2)

        else:
            raise ValueError('Invalid kind of operand')
